#pragma once


#include <string>
#include <map>
#include <functional>
#include <cassert>

template<typename T>
class flow_node
{
public:

	explicit flow_node(std::string id) :id__(id)
	{

	}

	void reset_value()
	{
		init__ = false;
	}

	void set_value(const T &data)
	{
		value__ = data;
		init__ = true;
	}

	void set_op(std::function<T(std::map<std::string,T>)> func)
	{
		op_set__ = true;
		func__ = func;
	}

	std::string get_id() const
	{
		return id__;
	}

	void add_node(flow_node<T>* p_node)
	{
		child_node__[p_node->get_id()] = p_node;
	}

	T get_value()
	{
		if (init__)
			return value__;
		child_value__.clear();
		for (auto &p : child_node__)
		{
			child_value__[p.first] = p.second->get_value();
		}
		if (op_set__)
		{
			value__ = func__(child_value__);
		}
		else
		{
			assert(op_set__);
			return value__;
		}
		init__ = true;
		return value__;
	}

	operator T()
	{
		return get_value();
	}

private:
	bool init__{ false };
	bool op_set__{ false };
	T value__;
	std::function<T(std::map<std::string, T>)> func__;
	std::map<std::string, flow_node<T>*> child_node__;
	std::map<std::string, T> child_value__;
	const std::string id__;
};


template<typename T>
class flow_manager
{
public:
	flow_node<T>* add_node(std::string id)
	{
		node_map__[id] = new flow_node<T>(id);
		return node_map__[id];
	}

	void reset()
	{
		for (auto p : node_map__)
		{
			p.second->reset_value();
		}
	}

	void clear()
	{
		for (auto p : node_map__)
		{
			delete p.second;
		}
		node_map__.clear();
	}

	flow_node<T>* get_node(std::string id)
	{
		if (node_map__.count(id) != 0)
			return node_map__[id];
		return nullptr;
	}
	
	virtual ~flow_manager()
	{
		clear();
	}

protected:
	std::map<std::string,flow_node<T>*> node_map__;
};


template<typename T>
class single_flow_layer
{
public:
	void set_child(single_flow_layer<T>* p)
	{
		child_layer__ = p;
	}
	
	void set_op(std::function<std::map<std::string, T>(std::map<std::string, T>)> func)
	{
		func__ = func;
		op_set__ = true;
	}

	std::map<std::string,T> get_values()
	{
		if (init__)
			return values__;
		values__.clear();
		auto child_values = child_layer__->get_values();
		if (op_set__)
		{
			values__ = func__(child_values);
		}
		else
		{
			assert(op_set__);
			return values__;
		}
		init__ = true;
		return values__;
	}

	void set_values(std::map<std::string, T> values)
	{
		values__ = values;
		init__ = true;
	}
private:
	single_flow_layer<T> * child_layer__{ nullptr };
	std::function<std::map<std::string, T>(std::map<std::string, T>)> func__;
	bool op_set__{ false };
	bool init__{ false };
	std::map<std::string, T> values__;
};